using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;
using log4net;
using Sedna.Core.Controller;
using Sedna.Core.Controls.Support;
using Sedna.Core.Support;
using Sedna.Core.Tiles.Contents;
using Sedna.Core.Tiles.Holder;
using Spring.Util;
using ControlBuilder=Sedna.Core.Controls.Support.ControlBuilder;
using PlaceHolder=Sedna.Core.Tiles.Holder.PlaceHolder;

namespace Sedna.Core.Tiles
{
	public class PlaceHolderManager
	{
        private ILog log = LogManager.GetLogger(typeof(PlaceHolderManager));
		private BasePage page;
		private IDictionary placeHolderContainers = new Hashtable();
		private IDictionary conatinerPlaceHolders = new Hashtable();
		private IDictionary defaultPlaceHolders = new Hashtable();

		public static void AssignContent(BasePage fromPage)
		{
			AssignContent(fromPage, fromPage);
		}

		public static void AssignContent(BasePage fromPage, BasePage toPage)
		{
			Assert.IsNotNull(fromPage, "From Page must be not null");
			Assert.IsNotNull(toPage, "To Page must be not null");

			PlaceHolderManager placeHolderManager = new PlaceHolderManager(toPage);

			/* PENDING: Contents from Controls */
			placeHolderManager.AssignContent(ArrayList.Repeat(fromPage.RootPlaceHolderContainer, 1));
			placeHolderManager.AssignContent(fromPage.Contents);

			placeHolderManager.BuildContent();						
		}

		#region Private Members
		
		private void BuildContent()
		{
			foreach (PlaceHolderContainer container in placeHolderContainers.Values)
			{
				container.BuildContent();
			}

			foreach (PlaceHolder placeHolder in defaultPlaceHolders.Values)
			{
				placeHolder.BuildContent();
			}
		}

		private void FindPlaceHolderContainers(ICollection source)
		{
			if (source == null)
			{
				return;
			}

			foreach (Object element in source)
			{
				if (element is IContentCollection)
				{
					FindPlaceHolderContainers((IContentCollection)element);
				}
				if ((element is PlaceHolderContainer) && !StringUtils.IsNullOrEmpty(((PlaceHolderContainer)element).ID))
				{
					placeHolderContainers.Add(((PlaceHolderContainer)element).ID, element);

					PlaceHolderContainer container = (PlaceHolderContainer)element;

					if (container.Layout != null && container.Layout.PlaceHolders != null)
					{
						foreach (PlaceHolder placeHolder in container.Layout.PlaceHolders)
						{
							if (!StringUtils.IsNullOrEmpty(placeHolder.ID))
							{
								conatinerPlaceHolders.Add(String.Format("{0}.{1}", container.ID, placeHolder.ID), placeHolder);
							}
						}
					}
				}
			}
		}

		private PlaceHolder FindPlaceHolder(String contentPlaceHolderId)
		{
			if (StringUtils.IsNullOrEmpty(contentPlaceHolderId))
			{
				return null;
			}

			if (conatinerPlaceHolders[contentPlaceHolderId] != null)
			{
				return (PlaceHolder)conatinerPlaceHolders[contentPlaceHolderId];
			}

			if (defaultPlaceHolders[contentPlaceHolderId] != null)
			{
				return (PlaceHolder)defaultPlaceHolders[contentPlaceHolderId];
			}

		    Control control = null;
            
            log.Debug("Searching for Sedna placeholder  "+contentPlaceHolderId +"...");
			if (page.HasMaster)
			{
                control = FindPlaceHolderInMaster(contentPlaceHolderId);
			} 
            else
			{
                control = page.FindControl(contentPlaceHolderId);
            }

            if (control == null)
            {
                log.Error("Cannot find Sedna PlaceHolder " + contentPlaceHolderId);
            }

			if (control is PlaceHolder)
			{
				defaultPlaceHolders.Add(control.ID, control);
			}

			return (control as PlaceHolder);
		}

	    private Control FindPlaceHolderInMaster(string contentPlaceHolderId)
	    {
	        Control control = null;
	        log.Debug("MasterPage found. Looking for placeholder.");
	        if (page.MasterPageContentPlaceHolderId != null)
	        {
	            //try to find specific placeholder in the master and then search for the sedna placeholder
	            ContentPlaceHolder ph = (ContentPlaceHolder) page.Master.FindControl(page.MasterPageContentPlaceHolderId);
	            control = ph.FindControl(contentPlaceHolderId);
	        }
	        else
	        {
	            //collect all placeholders on the master page and the try to find sedna placeholder there
	            IList<ContentPlaceHolder> masterPlaceHolders = getContentPlaceHolders(page.Master);
	            foreach (ContentPlaceHolder placeHolder in masterPlaceHolders)
	            {
	                control = placeHolder.FindControl(contentPlaceHolderId);
	                if (control != null) break;
	            }
	        }
	        return control;
	    }

	    /// <summary>
        /// Recursive method to find all ContentPlaceHolders
        /// </summary>
        /// <param name="root">root control to start search from</param>
        /// <returns>IList of ContentPlaceHolders</returns>
        private IList<ContentPlaceHolder> getContentPlaceHolders (Control root)
        {
            List<ContentPlaceHolder> result = new List<ContentPlaceHolder>();
            if (root is ContentPlaceHolder) //ent of tree, return self
            {
                result.Add((ContentPlaceHolder) root);
                return result;
            }
            if (root is UpdatePanel) //updatepanel found, search inside
            {
                UpdatePanel up = (UpdatePanel) root;
                return getContentPlaceHolders(up.ContentTemplateContainer);
            }
            foreach (Control currControl in root.Controls)
            {
               result.AddRange(getContentPlaceHolders(currControl));
            }
            return result;
        }

		private void AssignContent(ICollection contents)
		{
			if (contents == null)
			{
				return;
			}

			foreach (Object content in contents)
			{
				if (content is IContentCollection)
				{
					AssignContent((IContentCollection)content);
				}
				if ((content is IContent) && (contents is IContentCollection) && !StringUtils.IsNullOrEmpty(((IContentCollection)contents).PlaceHolderContainerId))
				{
					AssignContent((IContent)content, String.Format("{0}.{1}", ((IContentCollection)contents).PlaceHolderContainerId, ((IContent)content).ContentPlaceHolderId));
				}
				else if (content is IContent)
				{
					AssignContent((IContent)content, ((IContent)content).ContentPlaceHolderId);
				}
				else if ((content is Control) && !(content is IContent) && !(content is PlaceHolder))
				{
					AssignContent(((Control)content).Controls);
				}
			}
		}

		private void AssignContent(IContent content, String contentPlaceHolderId)
		{
		    PlaceHolder ph = FindPlaceHolder(contentPlaceHolderId);
			if (ph == null)
			{
				return;
			}

			IContent assignedContent = content;

			if (content is IConfiguration)
			{
				/* Try build control from configuration */
				Control controlFromConfiguration = ControlBuilder.BuildControl((IConfiguration)content);

				if (!(controlFromConfiguration is IContent))
				{
					return;
				}

				assignedContent = (IContent)controlFromConfiguration;
			}

			ph.AddContent(assignedContent);						
		}

		private PlaceHolderManager(BasePage page)
		{
			Assert.IsNotNull(page, "Page must be not null");
			
			this.page = page;

			FindPlaceHolderContainers(ArrayList.Repeat(page.RootPlaceHolderContainer, 1));
			FindPlaceHolderContainers(page.Contents);
		}

		#endregion
	}
}
